﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using JetBrains.Annotations;

namespace Kaleida.ServiceMonitor.Model
{
    public class ScriptWorkspace
    {
        private readonly IFilesystem filesystem;
        [NotNull] private IScriptSource currentScript = new NullScriptSource();
        private readonly List<IScriptSource> loadedScripts = new List<IScriptSource>();

        public event EventHandler CurrentScriptChanged;
        public event EventHandler CurrentScriptContentChanged;
        public event EventHandler LoadedScriptListChanged;

        public ScriptWorkspace() : this(new SystemFilesystem())
        {
        }

        internal ScriptWorkspace(IFilesystem filesystem)
        {
            this.filesystem = filesystem;
        }

        public ReadOnlyCollection<IScriptSource> LoadedScripts
        {
            get { return loadedScripts.AsReadOnly(); }
        }

        [NotNull]
        public IScriptSource CurrentScript
        {
            get { return currentScript; }
            set
            {
                if (value == null) throw new ArgumentNullException("value");

                if (!(value is NullScriptSource) && !loadedScripts.Contains(value))
                    throw new InvalidOperationException(string.Format("Cannot set current script to non-loaded script '{0}'", value.Name));

                currentScript = value;

                if (CurrentScriptChanged != null)
                    CurrentScriptChanged(this, EventArgs.Empty);
            }
        }

        public void NewScript([NotNull] string path)
        {
            if (path == null) throw new ArgumentNullException("path");
            const string contents = "// add operations here. e.g. \r\nhttp-get \"http://www.google.com\" must-contain \"I'm Feeling Lucky\"";

            if (filesystem.FileExists(path))
                throw new InvalidOperationException(string.Format("File '{0}' already exists", path));

            var newScript = ScriptSource.New(path, contents);
            AddScriptToInternalList(newScript);
            CurrentScript = newScript;
        }

        public void UnloadAllScripts()
        {
            CurrentScript = new NullScriptSource();
            foreach(var script in loadedScripts)
            {
                script.ScriptContentChanged -= ScriptContentChanged;
            }
            loadedScripts.Clear();

            OnLoadedScriptListChanged();
        }

        public void LoadScript([NotNull] string path)
        {
            if (path == null) throw new ArgumentNullException("path");

            var script = ScriptSource.Load(path, filesystem.ReadAllText(path));
            AddScriptToInternalList(script);
            CurrentScript = script;
        }

        public void SaveCurrentScript()
        {
            var script = currentScript;

            if (script == null) throw new InvalidOperationException("Cannot save. No script selected");

            script.Save(filesystem);
        }

        private void AddScriptToInternalList(ScriptSource newScript)
        {
            newScript.ScriptContentChanged += ScriptContentChanged;
            loadedScripts.Add(newScript);

            OnLoadedScriptListChanged();
        }

        private void ScriptContentChanged(object sender, EventArgs e)
        {
            if (sender == currentScript && CurrentScriptContentChanged != null)
                CurrentScriptContentChanged(this, EventArgs.Empty);
        }

        private void OnLoadedScriptListChanged()
        {
            if (LoadedScriptListChanged != null)
                LoadedScriptListChanged(this, EventArgs.Empty);
        }
    }
}
